﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MapAppLearning.GeocodeService;
using MapAppLearning.SearchService;
using MapAppLearning.ImageryService;
using MapAppLearning.RouteService;
using System.Text.RegularExpressions;
using SmartRoute.Data;

namespace MapAppLearning
{
    //Modified 3:59 PM
    class DataModule
    {
        /// <summary>
        /// A list of the each map, known as boundaries.
        /// </summary>
        private List<Map> boundaries;

        /// <summary>
        /// Getter and setter for boundaries list.
        /// </summary>
        public List<Map> Boundaries
        {
            get { return boundaries; }
            set { boundaries = value; }
        }

        /// <summary>
        /// Constructor for the data module.
        /// </summary>
        public DataModule()
        {
            boundaries = new List<Map>();
        }

        /// <summary>
        /// Adds an array of stops, each of which is passed as a string,
        ///     into a new map. This map is then added into the boundary.
        /// </summary>
        /// <param name="stopsStrings">String array of the all the stops.</param>
        public void addBoundary(IEnumerable<double> lon, IEnumerable<double> lat, IEnumerable<int> passengers)
        {
            Map newMap = new Map(lon, lat, passengers);
            boundaries.Add(newMap);
        }
        
        /// <summary>
        /// Returns the map located at the index in the list of boundaries.
        /// </summary>
        /// <param name="index">Index of desired map.</param>
        /// <returns>A Map object</returns>
        public Map accessMap(int index)
        {
            return boundaries[index];
        }
    }

    public class Map
    {
        

        //routes generated by algorithm
        private List<RouteResponse[]> producedByAlgorithm;

        public List<RouteResponse[]> ProducedByAlgorithm
        {
            get { return producedByAlgorithm; }
            set { producedByAlgorithm = value; }
        }
        /// <summary>
        /// An array of stops.
        /// </summary>
        private StopLocal[] stops;

        /// <summary>
        /// Getter and setter for stop object.
        /// </summary>
        public StopLocal[] Stops
        {
            get { return stops; }
            set { stops = value; }
        }

        /// <summary>
        /// A matrix of the shortest time and distance between every stop.
        ///     Used by the algorithm to compute fitness.
        /// </summary>
        private double[,] shortestDistanceArrayTime;

        /// <summary>
        /// Getter and setter for shortestDistanceArrayTime.
        /// </summary>
        public double[,] ShortestDistanceArrayTime
        {
            get { return shortestDistanceArrayTime; }
            set { shortestDistanceArrayTime = value; }
        }

        /// <summary>
        /// String array of stops. The addresses of the stops.
        /// </summary>
        private String[] stopsStrings;

        /// <summary>
        /// Getter and setter for stopStrings.
        /// </summary>
        public String[] StopsStrings
        {
            get { return stopsStrings; }
            set { stopsStrings = value; }
        }

        /// <summary>
        /// Matrix of routes returned from Bing maps.
        /// </summary>
        private RouteResponse[,] routes;

        /// <summary>
        /// Getter and setter for routes.
        /// </summary>
        public RouteResponse[,] Routes
        {
            get { return routes; }
            set { routes = value; }
        }

        /// <summary>
        /// The key used to access BingMap functions.
        /// </summary>
        private const string Key = "AhqjOSdmQlNyiAfMcr8HicreBdxgTnuVaiMGVFnP9x3l_12WQgLyV1pRrfIpECX5";

        /// <summary>
        /// Map constructor
        /// </summary>
        /// <param name="stopsStrings">String array of stops</param>
        public Map(IEnumerable<double> lon, IEnumerable<double> lat, IEnumerable<int> passengers)
        {
            stops = new StopLocal[lon.Count()];
            shortestDistanceArrayTime = new double[lon.Count(), lon.Count()];
            routes = new RouteResponse[lon.Count(), lon.Count()];
            
            this.initializeMap(lon, lat, passengers);
            producedByAlgorithm = new List<RouteResponse[]>();
        }

        /// <summary>
        /// Initializes map with each stop in Stop object.
        /// </summary>
        private void initializeMap(IEnumerable<double> lon, IEnumerable<double> lat, IEnumerable<int> passengers)
        {
            // Adds each stop, as a GeocodeAddress, to the Stop array.
            for (int i = 0; i < stops.Length; i++)
            {
                stops[i] = new StopLocal(i);
                stops[i].Passengers = passengers.ElementAt(i);
                MapAppLearning.RouteService.GeocodeLocation temp = new MapAppLearning.RouteService.GeocodeLocation();
                temp.Latitude = lat.ElementAt(i);
                temp.Longitude = lon.ElementAt(i);

                stops[i].Here = temp;
            }

            // Populate the shortestDistanceArrayTime array.
            for (int i = 0; i < stops.Length; i++)
            {
                for (int j = 0; j < stops.Length; j++)
                {

                    routes[i, j] = Map.RequestRoute(stops[i].Here, stops[j].Here);
                    shortestDistanceArrayTime[i, j] = routes[i, j].Result.Summary.TimeInSeconds;
                }
            }

            // Sets the number of passengers at each stop.
            this.setPassengers(passengers);
        }

        //this is a placeholder method, just sets the passengers at all stops to 5
        public void setPassengers(IEnumerable<int> passengers)
        {
            for (int i = 0; i < stops.Length; i++)
            {
                stops[i].Passengers = passengers.ElementAt(i);
            }
        }

        /// <summary>
        /// The following method obtained and modified from http://msdn.microsoft.com/en-us/library/dd221354.aspx
        ///     Gets a GeocodeAddress of an address.
        /// </summary>
        /// <param name="address">The address of a stop</param>
        /// <returns></returns>
        private static MapAppLearning.RouteService.GeocodeLocation GeocodeAddress(string address)
        {
            GeocodeRequest geocodeRequest = new GeocodeRequest();

            // Set the credentials using a valid Bing Maps key
            geocodeRequest.Credentials = new GeocodeService.Credentials();
            geocodeRequest.Credentials.ApplicationId = Key;

            // Set the full address query
            geocodeRequest.Query = address;

            // Set the options to only return high confidence results 
            ConfidenceFilter[] filters = new ConfidenceFilter[1];
            filters[0] = new ConfidenceFilter();
            filters[0].MinimumConfidence = GeocodeService.Confidence.High;

            // Add the filters to the options
            GeocodeOptions geocodeOptions = new GeocodeOptions();
            geocodeOptions.Filters = filters;
            geocodeRequest.Options = geocodeOptions;

            // Make the geocode request
            GeocodeServiceClient geocodeService = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
            GeocodeResponse geocodeResponse = geocodeService.Geocode(geocodeRequest);
            // make a location object from the response
            MapAppLearning.RouteService.GeocodeLocation results = new MapAppLearning.RouteService.GeocodeLocation();
            results.Latitude = geocodeResponse.Results[0].Locations[0].Latitude;
            results.Longitude = geocodeResponse.Results[0].Locations[0].Longitude;
            return results;
        }

        /// <summary>
        /// The following method obtained and modified from http://msdn.microsoft.com/en-us/library/dd221354.aspx
        ///     Gets a route from one stop to another.
        /// </summary>
        /// <param name="firstStop">The first stop in the route</param>
        /// <param name="lastStop">The last stop in the route</param>
        /// <returns>RouteResponse object</returns>
        private static RouteResponse RequestRoute(MapAppLearning.RouteService.GeocodeLocation firstStop, MapAppLearning.RouteService.GeocodeLocation lastStop)
        {
            RouteRequest routeRequest = new RouteRequest();

            // enter bing maps key
            routeRequest.Credentials = new RouteService.Credentials();
            routeRequest.Credentials.ApplicationId = Key;

            //waypoint array for beginning and end of route
            Waypoint[] waypoints = new Waypoint[2];
            waypoints[0] = new Waypoint();
            waypoints[0].Location = firstStop;
            waypoints[0].Description = "Start";

            //waypoint array for beginning and end of route
            waypoints[1] = new Waypoint();
            waypoints[1].Location = lastStop;
            waypoints[1].Description = "End";


            routeRequest.Options = new RouteOptions();
            routeRequest.Options.RoutePathType = RoutePathType.Points;

            
            routeRequest.Waypoints = waypoints;


            // Make the calculate route request
            RouteServiceClient routeService = new RouteServiceClient("BasicHttpBinding_IRouteService");
            RouteResponse routeResponse = routeService.CalculateRoute(routeRequest);

            return routeResponse;

        }

    }

    


    public class StopLocal
    {
        /// <summary>
        /// Number of passengers at a stop.
        private int passengers;

        /// <summary>
        /// Getter and setter for number of passengers
        /// </summary>
        public int Passengers
        {
            get { return passengers; }
            set { passengers = value; }
        }

        /// <summary>
        /// A GeocodeLocation object.
        /// </summary>
        private MapAppLearning.RouteService.GeocodeLocation here;

        /// <summary>
        /// Getter and setter for Geocode Location object.
        /// </summary>
        public MapAppLearning.RouteService.GeocodeLocation Here
        {
            get { return here; }
            set { here = value; }
        }

        /// <summary>
        /// An ID number for a stop.
        /// </summary>
        private int id;

        /// <summary>
        /// Getter and setter for ID number.
        /// </summary>
        public int Id
        {
            get { return id; }
            set { id = value; }
        }

        /// <summary>
        /// Bus ID for a stop.
        /// </summary>
        private int busId;

        /// <summary>
        /// Getter and setter for busId number.
        /// </summary>
        public int BusId
        {
            get { return busId; }
            set { busId = value; }
        }

        /// <summary>
        /// X coordinate of the stop.
        /// </summary>
        private int x;

        /// <summary>
        /// Getter and setter for x coordinate.
        /// </summary>
        public int X
        {
            get { return x; }
            set { x = value; }
        }

        /// <summary>
        /// Y coordinate of the stop.
        /// </summary>
        private int y;

        /// <summary>
        /// Getter and setter for y coordinate.
        /// </summary>
        public int Y
        {
            get { return y; }
            set { y = value; }
        }

        /// <summary>
        /// Stop constructor.
        /// </summary>
        /// <param name="id">ID for the stop.</param>
        public StopLocal(int id)
        {
            this.passengers = 0;
            this.id = id;
            busId = -1;
            here = null;

        }

    }

}
